#include "stdafx.h"
#include "Prefs.h"

#include "getopt.h"
#include "Utils.h"

#ifdef WIN32
#include <shlobj.h> /* SHGetFolderPath() */
#endif

/* ******************************************* */
Prefs * gPrefs=NULL;
#define CONST_DEFAULT_DATA_DIR      "data"

#define DEFAULT_PID_PATH        "/var/tmp/"
#define CONST_DEFAULT_WRITABLE_DIR  "/var/tmp"
#define CONST_DEFAULT_INSTALL_DIR   "/usr/local/share/mines"

/* SVN Release */
#define COMHUB_SVN_RELEASE "r328"

/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT ""

/* Architecture of this host */
#define PACKAGE_MACHINE "x86_64"
/* SVN release of this package */
#define PACKAGE_RELEASE "r328"
/* Define to the version of this package. */
#define PACKAGE_VERSION "1.6"

Prefs::Prefs( char * app) {
	char * cp;
	if ((cp = strrchr(app, CONST_PATH_SEP)) != NULL)
		strcpy(mProgram ,cp + 1 );
	else strcpy(mProgram ,app);
	if ((cp = strrchr(mProgram, '.')) != NULL)	cp[0]=0;
	memset(mServer,0,sizeof(mServer));
	strcpy(mServer,"127.0.0.1");
	mPort = 9200;
	strcpy(mMethod,"POST");
	memset(mUrl,0,sizeof(mUrl));
	strcpy(mDomain,"localhost");
	mHeaders = "";
    mLogFd = NULL;
	mShowMode = 0;
}

/* ******************************************* */

Prefs::~Prefs() {
  //printf("free 1 ");
  if(mLogFd) fclose(mLogFd);
  //printf("5 ");
  
}

/* ******************************************* */
void Prefs::Usage_s() {
  printf("%s %s v.%s (%s) - (C) 2014-06 atdm.liao\n\n"
	 "Usage:\n"
	 "  %s [-v <0-6>] [-h] [-f <defaults file>]  [-l <logFile name>] [-p port] [-m <method>]\n" 
	 "     [-d <domain name>] [-s <server name>] [-o <file name>] -u <metadata> -d <dataFile name> or data \n"
	 ,mProgram, PACKAGE_MACHINE, PACKAGE_VERSION, COMHUB_SVN_RELEASE,mProgram
	 );
}
/* C-binding needed by Win32 service call */
void Prefs::Usage() {

  printf("%s %s v.%s (%s) - (C) 2014-06 atdm.liao\n\n"
	 "Usage:\n"
	 "  %s [-v <0-6>] [-h] [-f <defaults file>]  [-l <logFile name>] [-p port] [-m <method>] \n"
	 "     [-d <domain name>] [-s <server name>] [-o <file name>] -u <metadata> -d <dataFile name> or data\n"
	 "\n"
	 "Options:\n"
	 "[--conf|-f]     <file name>    | Use the specified defaults file\n"
	 "[--log|-l]      <file name>    | Save log to file\n"
	 "[--block|-b]    <number>       | show field of output,1: time , 2: date,4: file\n"
	 "[--server|-s]   <ES host>      | ES address, default 127.0.0.1\n"
	 "[--port|-p]                    | ES HTTP Port, default 9200\n"
	 "[--name|-n]     <domain name>  | Domain Name of ES, default localhost\n"
	 "[--mothod|-m]   <POST/PUT>     | method,default POST\n"
	 "[--url|-u]      <metadata>     | metadata, /{index}/{type}/{id}\n"
	 "[--head|-e]    <string>        | add a header\n"
	 "[--data|-d]     <file name>    | data file to be sent\n"
	 "[--verbose|-v]   <number>      | Verbose tracing, [0-6]\n"
	 "[--help|-h]                    | Help\n"
	 ,mProgram, PACKAGE_MACHINE, PACKAGE_VERSION, COMHUB_SVN_RELEASE,mProgram
	 );

  printf("\n");

  exit(0);
}

/* ******************************************* */

static const struct option long_options[] = {
  { "server",                         required_argument, NULL, 's' },
  { "port",                         required_argument, NULL, 'p' },
  { "log",                          required_argument, NULL, 'o' },
  { "block",                          required_argument, NULL, 'b' },
  { "method",                       required_argument, NULL, 'm' },
  { "name",                          required_argument, NULL, 'n' },
  { "url",                          required_argument, NULL, 'u' },
  { "data",				            required_argument, NULL, 'd' },
  { "head",				            required_argument, NULL, 'e' },
  { "conf",				            required_argument, NULL, 'f' },
  { "verbose",                           required_argument,       NULL, 'v' },
  { "help",                              no_argument,       NULL, 'h' },
  /* End of options */
  { NULL,                                no_argument,       NULL,  0 }
};

/* ******************************************* */
static int GetFileString(const char * name,TString& out)
{
  FILE * fp = fopen(name,"rb");
  if( fp == NULL )	  return -1;
  
  fseek(fp,0,SEEK_END);
  int length = ftell(fp);
  out.Remove();
  char * dest = out.GetBuffer(length);
  fseek(fp,0,SEEK_SET);
  fread(dest,1,length,fp);
  fclose(fp);
  out.ReleaseBuffer();
	return length;
}

int Prefs::SetOption(int optkey, char *optarg) {
	int k;
	//i B r p b   
  switch(optkey) {
  case 'f':
	  if( strlen(optarg)>1 ) return LoadFromFile(optarg);
	  return -1;
	  break;
  case 'v':
	  k= atoi(optarg);
	  if( k<0 )k=0;
	  if( k>MAX_TRACE_LEVEL ) k= MAX_TRACE_LEVEL;
    Trace::set_trace_level(k);
    break;
  case 'h':
    Usage();
    break;
  case 's':
	  if( optarg ) strncpy(mServer,optarg,sizeof(mServer));
	  break;
  case 'b':
	  mShowMode = atoi(optarg);
	  if( mShowMode <= 0 ) mShowMode = 0;
	  break;
  case 'p':
	  mPort = atoi(optarg);
	  if( mPort <= 0 ) mPort = 9200;
	  break;
  case 'n':
	  if( optarg) strncpy(mDomain,optarg,sizeof(mDomain));
	  break;
  case 'm':
	  if( optarg ) strncpy(mMethod,optarg,sizeof(mMethod));
	  break;
  case 'u':
	  if( optarg ) strncpy(mUrl,optarg,sizeof(mUrl));
	  break;
  case 'd':	  
	  if( GetFileString(optarg,mData)<0 ){
		  printf("%s: can not open %s for input\n",mProgram,optarg);
		  return -1;
	  }
	  break;
  case 'e':
	  mHeaders.Append(optarg);
	  mHeaders.Append("\r\n");
	  break;
  case 'l':
    if( mLogFd ) fclose(mLogFd);
    mLogFd = fopen(optarg, "a");
	if(mLogFd)
		printf("%s: set log file to %s\n",mProgram,optarg);
	else
		printf("%s: can not open log file %s\n",mProgram,optarg);
    break;
  default:
    return(-1);
  }

  return(0);
}
static void JsonString(TString & dest, const char *str)
{
static const char *json_hex_chars = "0123456789abcdef";
  int pos = 0;
  unsigned char c;
  while (str[pos]) {
    c = str[pos++];
    switch(c) {
	case '/':
		dest.Append('\\');
		dest.Append(c);
		break;
/*	case '\\':
		dest[offset++]='\\';
		c= str[pos];
		if( c=='\\' || c=='"' ||c=='/' || c==''' || c=='b' || c=='f' || c=='t' || c=='n'||c=='r'||c=='u' ){
			pos++;
		    dest[offset++]=c;
		} else dest[offset++]='\\';
		break;
*/	case '\b':
		dest.Append('\\');
		dest.Append('b');
		break;
	case '\f':
		dest.Append('\\');
		dest.Append('f');
		break;
	case '\t':
		dest.Append('\\');
		dest.Append('t');
		break;
	case '\n':
		dest.Append('\\');
		dest.Append('n');
		break;
	case '\r':
		dest.Append('\\');
		dest.Append('r');
		break;
    default:
      if(c < ' ') {
			dest.Append('\\');
			dest.Append('u');
		  dest.Append(json_hex_chars[c >> 4]);
		  dest.Append(json_hex_chars[c & 0xf]);
	  } else dest.Append(c);
    }
  }
}


/* ******************************************* */
/* ******************************************* */

int Prefs::LoadFromCLI(int argc, char *argv[]) {
  int c;
  char buff[128];
#ifdef WIN32
  sprintf(buff,"%s.conf",mProgram);
#else
  sprintf(buff,"/etc/%s.conf",mProgram);
#endif

  FILE * fd = fopen(buff, "r");
  if( fd ){
	  fclose(fd);
	  LoadFromFile(buff);
  }
  while((c = getopt_long(argc, argv, "s:p:n:m:u:l:f:d:v:e:h",
			 long_options, NULL)) != EOF ) {
	if( c== '?' ) {  Usage_s(); return -1; }
    if( SetOption(c, (char *)optarg)<0 ) return -1;
  }
  if( mServer[0] == 0 ) {
	    printf("%s: option require server\n",mProgram);
		Usage_s();
		return -1;
  }
  if( mUrl[0] ==0 ) {
	    printf("%s: option require metadata\n",mProgram);
		Usage_s();
		return -1;
  }
  if( optind < argc ){
	  mData.Remove();
	  mData.Append("{ ");
	  for(int i=optind;i<argc;i++){
		  mData.Append(' ');
		  JsonString(mData,argv[i]);
	  }
	  mData.Append(" }");
  }
  if( strcmp(mMethod,"POST")==0 ){
	  if( mData.GetLength()<2 ) {
			printf("%s: option require data\n",mProgram);
			Usage_s();
			return -1;
	  }
  }
  //Utils::GetCString(argv[optind],mData,strlen(argv[optind])+1);

  return 0;
}

/* ******************************************* */

int Prefs::LoadFromFile(const char *path) {
  char buffer[512], *line, *key, *value;
  FILE *fd;
  const struct option *opt;

  fd = fopen(path, "r");

  if(fd == NULL) {
    Trace::traceEvent(TRACE_WARNING,"Config file %s not found", path);
    return(-1);
  }

  while(fd) {
    if(!(line = fgets(buffer, sizeof(buffer), fd)))
      break;

    line = Utils::Trim(line);

    if(strlen(line) < 1 || line[0] == '#')
      continue;

    key = line;
    key = Utils::Trim(key);
    
    value = strrchr(line, '=');

    /* Fallback to space */
    if(value == NULL) value = strrchr(line, ' ');
      
    if(value == NULL)
      value = &line[strlen(line)]; /* empty */
    else
      value[0] = 0, value = &value[1];
    value = Utils::Trim(value);

    //if(strlen(key) > 2) key = &key[2];
    //else key = &key[1];

    opt = long_options;
    while (opt->name != NULL) {
      if((strcmp(opt->name, key) == 0)
	  || ((key[1] == '\0') && (opt->val == key[0]))) {
        if( SetOption(opt->val, value)<0 ) return -1;
        break;
      }
      opt++;
    }
  }

  fclose(fd);
  return 1;
  //return(checkOptions());
}

